﻿#region Copyright (c) 02/16/2012, Olaf Kober <amarok.projects@gmail.com>
//================================================================================
//	Permission is hereby granted, free of charge, to any person obtaining a copy
//	of this software and associated documentation files (the "Software"), to deal
//	in the Software without restriction, including without limitation the rights
//	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//	copies of the Software, and to permit persons to whom the Software is
//	furnished to do so, subject to the following conditions:
//
//	The above copyright notice and this permission notice shall be included in
//	all copies or substantial portions of the Software.
//
//	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//	THE SOFTWARE.
//================================================================================
#endregion

#if TESTS

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using NUnit.Framework;


namespace Amarok.Presentation
{
	[TestFixture]
	public class Test_ViewModelCollection
	{
		private readonly List<String> mPropertyChanges =
			new List<String>();
		private readonly List<NotifyCollectionChangedEventArgs> mCollectionChanges =
			new List<NotifyCollectionChangedEventArgs>();

		private void _HandlePropertyChanged(Object sender, PropertyChangedEventArgs e)
		{
			mPropertyChanges.Add(e.PropertyName);
		}

		private void _HandleCollectionChanged(Object sender, NotifyCollectionChangedEventArgs e)
		{
			mCollectionChanges.Add(e);
		}

		private void _Subscribe(ViewModelCollection<String> collection)
		{
			mPropertyChanges.Clear();
			mCollectionChanges.Clear();

			collection.PropertyChanged += _HandlePropertyChanged;
			collection.CollectionChanged += _HandleCollectionChanged;
		}

		private void _Unsubscribe(ViewModelCollection<String> collection)
		{
			collection.PropertyChanged -= _HandlePropertyChanged;
			collection.CollectionChanged -= _HandleCollectionChanged;
		}

		private void _Reset()
		{
			mPropertyChanges.Clear();
			mCollectionChanges.Clear();
		}


		// --- TESTS ---


		#region ++ Public Interface (Construction) ++

		[Test]
		public void Test_Construction()
		{
			var collection = new ViewModelCollection<String>();

			Assert.That(collection.Count, Is.EqualTo(0));
			Assert.That(collection.HasItems, Is.False);
		}

		[Test]
		public void Test_Construction_IEnumerable()
		{
			var data = new[] { "AA", "BB", "CC" };
			var collection = new ViewModelCollection<String>(data);

			Assert.That(collection.Count, Is.EqualTo(3));
			Assert.That(collection.HasItems, Is.True);
			Assert.That(collection[0], Is.EqualTo("AA"));
			Assert.That(collection[1], Is.EqualTo("BB"));
			Assert.That(collection[2], Is.EqualTo("CC"));
		}

		[Test]
		public void Test_Construction_IEnumerable_Empty()
		{
			var data = new String[0];
			var collection = new ViewModelCollection<String>(data);

			Assert.That(collection.Count, Is.EqualTo(0));
			Assert.That(collection.HasItems, Is.False);
		}

		#endregion

		#region ++ Public Interface (Events) ++

		[Test]
		public void Test_Events()
		{
			var collection = new ViewModelCollection<String>();
			_Subscribe(collection);

			collection.Add("AA");

			Assert.That(mPropertyChanges.Count, Is.EqualTo(3));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(1));

			_Reset();
			_Unsubscribe(collection);
			collection.Add("BB");

			Assert.That(mPropertyChanges.Count, Is.EqualTo(0));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(0));
		}

		#endregion

		#region ++ Public Interface (Add, AddRange) ++

		[Test]
		public void Test_Add()
		{
			var collection = new ViewModelCollection<String>();
			_Subscribe(collection);

			// initial state
			Assert.That(collection.Count, Is.EqualTo(0));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(0));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(0));

			// first item added
			collection.Add("AA");

			Assert.That(collection.Count, Is.EqualTo(1));
			Assert.That(collection[0], Is.EqualTo("AA"));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(3));
			Assert.That(mPropertyChanges.AsString(";"), Is.EqualTo("Count;Item[];HasItems"));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(1));
			Assert.That(mCollectionChanges[0].Action, Is.EqualTo(NotifyCollectionChangedAction.Add));
			Assert.That(mCollectionChanges[0].NewItems, Is.EquivalentTo(new[] { "AA" }));
			Assert.That(mCollectionChanges[0].NewStartingIndex, Is.EqualTo(0));
			Assert.That(mCollectionChanges[0].OldItems, Is.Null);
			Assert.That(mCollectionChanges[0].OldStartingIndex, Is.EqualTo(-1));

			// second item added
			_Reset();
			collection.Add("BB");

			Assert.That(collection.Count, Is.EqualTo(2));
			Assert.That(collection[0], Is.EqualTo("AA"));
			Assert.That(collection[1], Is.EqualTo("BB"));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(3));
			Assert.That(mPropertyChanges.AsString(";"), Is.EqualTo("Count;Item[];HasItems"));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(1));
			Assert.That(mCollectionChanges[0].Action, Is.EqualTo(NotifyCollectionChangedAction.Add));
			Assert.That(mCollectionChanges[0].NewItems, Is.EquivalentTo(new[] { "BB" }));
			Assert.That(mCollectionChanges[0].NewStartingIndex, Is.EqualTo(1));
			Assert.That(mCollectionChanges[0].OldItems, Is.Null);
			Assert.That(mCollectionChanges[0].OldStartingIndex, Is.EqualTo(-1));
		}

		[Test]
		public void Test_Add_Null()
		{
			var collection = new ViewModelCollection<String>();
			_Subscribe(collection);

			// initial state
			Assert.That(collection.Count, Is.EqualTo(0));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(0));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(0));

			// null added
			collection.Add(null);

			Assert.That(collection.Count, Is.EqualTo(1));
			Assert.That(collection[0], Is.EqualTo(null));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(3));
			Assert.That(mPropertyChanges.AsString(";"), Is.EqualTo("Count;Item[];HasItems"));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(1));
			Assert.That(mCollectionChanges[0].Action, Is.EqualTo(NotifyCollectionChangedAction.Add));
			Assert.That(mCollectionChanges[0].NewItems, Is.EquivalentTo(new String[] { null }));
			Assert.That(mCollectionChanges[0].NewStartingIndex, Is.EqualTo(0));
			Assert.That(mCollectionChanges[0].OldItems, Is.Null);
			Assert.That(mCollectionChanges[0].OldStartingIndex, Is.EqualTo(-1));
		}

		[Test]
		public void Test_Add_Duplicates()
		{
			var collection = new ViewModelCollection<String>();
			_Subscribe(collection);

			// initial state
			Assert.That(collection.Count, Is.EqualTo(0));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(0));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(0));

			// item added twice
			collection.Add("AA");
			collection.Add("AA");

			Assert.That(collection.Count, Is.EqualTo(2));
			Assert.That(collection[0], Is.EqualTo("AA"));
			Assert.That(collection[1], Is.EqualTo("AA"));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(6));
			Assert.That(mPropertyChanges.AsString(";"), Is.EqualTo("Count;Item[];HasItems;Count;Item[];HasItems"));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(2));
			Assert.That(mCollectionChanges[0].Action, Is.EqualTo(NotifyCollectionChangedAction.Add));
			Assert.That(mCollectionChanges[0].NewItems, Is.EquivalentTo(new String[] { "AA" }));
			Assert.That(mCollectionChanges[0].NewStartingIndex, Is.EqualTo(0));
			Assert.That(mCollectionChanges[0].OldItems, Is.Null);
			Assert.That(mCollectionChanges[0].OldStartingIndex, Is.EqualTo(-1));
			Assert.That(mCollectionChanges[1].Action, Is.EqualTo(NotifyCollectionChangedAction.Add));
			Assert.That(mCollectionChanges[1].NewItems, Is.EquivalentTo(new String[] { "AA" }));
			Assert.That(mCollectionChanges[1].NewStartingIndex, Is.EqualTo(1));
			Assert.That(mCollectionChanges[1].OldItems, Is.Null);
			Assert.That(mCollectionChanges[1].OldStartingIndex, Is.EqualTo(-1));
		}

		[Test]
		public void Test_Add_Reentrancy()
		{
			var collection = new ViewModelCollection<String>();

			collection.CollectionChanged += (sender, e) =>
				{
					// not allowed
					collection.Add("TEST");
				};

			Assert.Throws<InvalidOperationException>(() =>
				{
					collection.Add("AA");
				});
		}


		[Test]
		public void Test_AddRange()
		{
			var collection = new ViewModelCollection<String>();
			_Subscribe(collection);

			// initial state
			Assert.That(collection.Count, Is.EqualTo(0));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(0));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(0));

			// two items added
			collection.AddRange(new[] { "AA", "BB" });

			Assert.That(collection.Count, Is.EqualTo(2));
			Assert.That(collection[0], Is.EqualTo("AA"));
			Assert.That(collection[1], Is.EqualTo("BB"));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(3));
			Assert.That(mPropertyChanges.AsString(";"), Is.EqualTo("Count;Item[];HasItems"));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(1));
			Assert.That(mCollectionChanges[0].Action, Is.EqualTo(NotifyCollectionChangedAction.Add));
			Assert.That(mCollectionChanges[0].NewItems, Is.EquivalentTo(new[] { "AA", "BB" }));
			Assert.That(mCollectionChanges[0].NewStartingIndex, Is.EqualTo(0));
			Assert.That(mCollectionChanges[0].OldItems, Is.Null);
			Assert.That(mCollectionChanges[0].OldStartingIndex, Is.EqualTo(-1));

			// three items added
			_Reset();
			collection.AddRange(new[] { "CC", null, "DD" });

			Assert.That(collection.Count, Is.EqualTo(5));
			Assert.That(collection[0], Is.EqualTo("AA"));
			Assert.That(collection[1], Is.EqualTo("BB"));
			Assert.That(collection[2], Is.EqualTo("CC"));
			Assert.That(collection[3], Is.EqualTo(null));
			Assert.That(collection[4], Is.EqualTo("DD"));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(3));
			Assert.That(mPropertyChanges.AsString(";"), Is.EqualTo("Count;Item[];HasItems"));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(1));
			Assert.That(mCollectionChanges[0].Action, Is.EqualTo(NotifyCollectionChangedAction.Add));
			Assert.That(mCollectionChanges[0].NewItems, Is.EquivalentTo(new[] { "CC", null, "DD" }));
			Assert.That(mCollectionChanges[0].NewStartingIndex, Is.EqualTo(2));
			Assert.That(mCollectionChanges[0].OldItems, Is.Null);
			Assert.That(mCollectionChanges[0].OldStartingIndex, Is.EqualTo(-1));
		}

		[Test]
		public void Test_AddRange_NonListCollection()
		{
			var collection = new ViewModelCollection<String>();
			_Subscribe(collection);

			// initial state
			Assert.That(collection.Count, Is.EqualTo(0));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(0));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(0));

			// two items added
			var set = new HashSet<String>(new[] { "AA", "BB" });
			collection.AddRange(set);

			Assert.That(collection.Count, Is.EqualTo(2));
			Assert.That(collection[0], Is.EqualTo("AA"));
			Assert.That(collection[1], Is.EqualTo("BB"));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(3));
			Assert.That(mPropertyChanges.AsString(";"), Is.EqualTo("Count;Item[];HasItems"));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(1));
			Assert.That(mCollectionChanges[0].Action, Is.EqualTo(NotifyCollectionChangedAction.Add));
			Assert.That(mCollectionChanges[0].NewItems, Is.EquivalentTo(new[] { "AA", "BB" }));
			Assert.That(mCollectionChanges[0].NewStartingIndex, Is.EqualTo(0));
			Assert.That(mCollectionChanges[0].OldItems, Is.Null);
			Assert.That(mCollectionChanges[0].OldStartingIndex, Is.EqualTo(-1));
		}

		[Test]
		public void Test_AddRange_EmptyCollection()
		{
			var collection = new ViewModelCollection<String>();
			_Subscribe(collection);

			// initial state
			Assert.That(collection.Count, Is.EqualTo(0));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(0));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(0));

			// empty collection added
			collection.AddRange(new String[0]);

			Assert.That(collection.Count, Is.EqualTo(0));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(0));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(0));
		}

		[Test]
		public void Test_AddRange_Null()
		{
			var collection = new ViewModelCollection<String>();

			Assert.Throws<ArgumentNullException>(() =>
				{
					collection.AddRange(null);
				});
		}

		[Test]
		public void Test_AddRange_Reentrancy()
		{
			var collection = new ViewModelCollection<String>();

			collection.CollectionChanged += (sender, e) =>
				{
					// not allowed
					collection.Add("TEST");
				};

			Assert.Throws<InvalidOperationException>(() =>
				{
					collection.AddRange(new[] { "AA", "BB" });
				});
		}

		#endregion

		#region ++ Public Interface (Clear) ++

		[Test]
		public void Test_Clear()
		{
			var collection = new ViewModelCollection<String>(new[] { "AA", "BB", "CC" });
			_Subscribe(collection);

			// initial state
			Assert.That(collection.Count, Is.EqualTo(3));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(0));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(0));

			// clear items
			collection.Clear();

			Assert.That(collection.Count, Is.EqualTo(0));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(3));
			Assert.That(mPropertyChanges.AsString(";"), Is.EqualTo("Count;Item[];HasItems"));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(1));
			Assert.That(mCollectionChanges[0].Action, Is.EqualTo(NotifyCollectionChangedAction.Reset));
			Assert.That(mCollectionChanges[0].NewItems, Is.Null);
			Assert.That(mCollectionChanges[0].NewStartingIndex, Is.EqualTo(-1));
			Assert.That(mCollectionChanges[0].OldItems, Is.Null);
			Assert.That(mCollectionChanges[0].OldStartingIndex, Is.EqualTo(-1));
		}

		[Test]
		public void Test_Clear_EmptyCollection()
		{
			var collection = new ViewModelCollection<String>();
			_Subscribe(collection);

			// initial state
			Assert.That(collection.Count, Is.EqualTo(0));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(0));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(0));

			// clear items
			collection.Clear();

			Assert.That(collection.Count, Is.EqualTo(0));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(0));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(0));
		}

		[Test]
		public void Test_Clear_Reentrancy()
		{
			var collection = new ViewModelCollection<String>(new[] { "AA", "BB" });

			collection.CollectionChanged += (sender, e) =>
			{
				// not allowed
				collection.Add("TEST");
			};

			Assert.Throws<InvalidOperationException>(() =>
			{
				collection.Clear();
			});
		}

		#endregion

		#region ++ Public Interface (Exists) ++

		[Test]
		public void Test_Exists()
		{
			var collection = new ViewModelCollection<String>(new[] { "AA", "BB", null, "AA" });

			Assert.That(collection.Exists(item => item == "AA"), Is.True);
			Assert.That(collection.Exists(item => item == "BB"), Is.True);
			Assert.That(collection.Exists(item => item == "CC"), Is.False);
			Assert.That(collection.Exists(item => item == null), Is.True);
		}

		[Test]
		public void Test_Exists_NullMatch()
		{
			var collection = new ViewModelCollection<String>(new[] { "AA", "BB", null, "AA" });

			Assert.Throws<ArgumentNullException>(() =>
				{
					collection.Exists(null);
				});

		}

		#endregion

		#region ++ Public Interface (Find, FindLast, FindAll) ++

		[Test]
		public void Test_Find()
		{
			var collection = new ViewModelCollection<Tuple<String, Int32>>();
			collection.Add(new Tuple<String, Int32>("AA", 1));
			collection.Add(new Tuple<String, Int32>("BB", 2));
			collection.Add(new Tuple<String, Int32>("AA", 3));
			collection.Add(new Tuple<String, Int32>(null, 4));
			collection.Add(new Tuple<String, Int32>("AA", 5));

			Assert.That(collection.Find(item => item.Item1 == "AA").Item2, Is.EqualTo(1));
			Assert.That(collection.Find(item => item.Item1 == "BB").Item2, Is.EqualTo(2));
			Assert.That(collection.Find(item => item.Item1 == null).Item2, Is.EqualTo(4));
			Assert.That(collection.Find(item => item.Item1 == "CC"), Is.Null);
		}

		[Test]
		public void Test_Find_NullMatch()
		{
			var collection = new ViewModelCollection<String>();

			Assert.Throws<ArgumentNullException>(() =>
				{
					collection.Find(null);
				});
		}


		[Test]
		public void Test_FindLast()
		{
			var collection = new ViewModelCollection<Tuple<String, Int32>>();
			collection.Add(new Tuple<String, Int32>("AA", 1));
			collection.Add(new Tuple<String, Int32>("BB", 2));
			collection.Add(new Tuple<String, Int32>("AA", 3));
			collection.Add(new Tuple<String, Int32>(null, 4));
			collection.Add(new Tuple<String, Int32>("AA", 5));

			Assert.That(collection.FindLast(item => item.Item1 == "AA").Item2, Is.EqualTo(5));
			Assert.That(collection.FindLast(item => item.Item1 == "BB").Item2, Is.EqualTo(2));
			Assert.That(collection.FindLast(item => item.Item1 == null).Item2, Is.EqualTo(4));
			Assert.That(collection.FindLast(item => item.Item1 == "CC"), Is.Null);
		}

		[Test]
		public void Test_FindLast_NullMatch()
		{
			var collection = new ViewModelCollection<String>();

			Assert.Throws<ArgumentNullException>(() =>
				{
					collection.FindLast(null);
				});
		}


		[Test]
		public void Test_FindAll()
		{
			var collection = new ViewModelCollection<Tuple<String, Int32>>();
			collection.Add(new Tuple<String, Int32>("AA", 1));
			collection.Add(new Tuple<String, Int32>("BB", 2));
			collection.Add(new Tuple<String, Int32>("AA", 3));
			collection.Add(new Tuple<String, Int32>(null, 4));
			collection.Add(new Tuple<String, Int32>("AA", 5));

			var result1 = collection.FindAll(item => item.Item1 == "AA");
			Assert.That(result1.Select(item => item.Item2), Is.EquivalentTo(new[] { 1, 3, 5 }));

			var result2 = collection.FindAll(item => item.Item1 == "BB");
			Assert.That(result2.Select(item => item.Item2), Is.EquivalentTo(new[] { 2 }));

			var result3 = collection.FindAll(item => item.Item1 == null);
			Assert.That(result3.Select(item => item.Item2), Is.EquivalentTo(new[] { 4 }));

			var result4 = collection.FindAll(item => item.Item1 == "CC");
			Assert.That(result4.Select(item => item.Item2), Is.Empty);
		}

		[Test]
		public void Test_FindAll_NullMatch()
		{
			var collection = new ViewModelCollection<String>();

			Assert.Throws<ArgumentNullException>(() =>
				{
					collection.FindAll(null);
				});
		}

		#endregion

		#region ++ Public Interface (FindIndex, FindLastIndex) ++

		[Test]
		public void Test_FindIndex()
		{
			var collection = new ViewModelCollection<String>(new[] { 
				"AA", "BB", null, "CC", "AA" });

			Assert.That(collection.FindIndex(item => item == "AA"), Is.EqualTo(0));
			Assert.That(collection.FindIndex(item => item == "BB"), Is.EqualTo(1));
			Assert.That(collection.FindIndex(item => item == "CC"), Is.EqualTo(3));
			Assert.That(collection.FindIndex(item => item == null), Is.EqualTo(2));
			Assert.That(collection.FindIndex(item => item == "DD"), Is.EqualTo(-1));
		}

		[Test]
		public void Test_FindIndex_NullMatch()
		{
			var collection = new ViewModelCollection<String>();

			Assert.Throws<ArgumentNullException>(() =>
				{
					collection.FindIndex(null);
				});
		}

		[Test]
		public void Test_FindIndex_StartIndex()
		{
			var collection = new ViewModelCollection<String>(new[] { 
				"AA", "BB", null, "CC", "AA" });

			Assert.That(collection.FindIndex(0, item => item == "AA"), Is.EqualTo(0));
			Assert.That(collection.FindIndex(1, item => item == "AA"), Is.EqualTo(4));
			Assert.That(collection.FindIndex(0, item => item == "BB"), Is.EqualTo(1));
			Assert.That(collection.FindIndex(1, item => item == "BB"), Is.EqualTo(1));
			Assert.That(collection.FindIndex(2, item => item == "BB"), Is.EqualTo(-1));
			Assert.That(collection.FindIndex(1, item => item == "CC"), Is.EqualTo(3));
			Assert.That(collection.FindIndex(2, item => item == null), Is.EqualTo(2));
			Assert.That(collection.FindIndex(0, item => item == "DD"), Is.EqualTo(-1));
		}

		[Test]
		public void Test_FindIndex_StartIndex_NullMatch()
		{
			var collection = new ViewModelCollection<String>();

			Assert.Throws<ArgumentNullException>(() =>
				{
					collection.FindIndex(0, null);
				});
		}

		[Test]
		public void Test_FindIndex_StartIndex_InvalidStartIndex()
		{
			var collection = new ViewModelCollection<String>(new[] { "AA", "BB", "CC" });

			Assert.Throws<ArgumentOutOfRangeException>(() =>
				{
					collection.FindIndex(-1, item => true);
				});
			Assert.Throws<ArgumentOutOfRangeException>(() =>
				{
					collection.FindIndex(4, item => true);
				});
		}

		[Test]
		public void Test_FindIndex_StartIndex_Count()
		{
			var collection = new ViewModelCollection<String>(new[] { 
				"AA", "BB", null, "CC", "AA" });

			Assert.That(collection.FindIndex(0, 1, item => item == "AA"), Is.EqualTo(0));
			Assert.That(collection.FindIndex(0, 0, item => item == "AA"), Is.EqualTo(-1));
			Assert.That(collection.FindIndex(1, 3, item => item == "AA"), Is.EqualTo(-1));
			Assert.That(collection.FindIndex(0, 2, item => item == "BB"), Is.EqualTo(1));
			Assert.That(collection.FindIndex(0, 1, item => item == "BB"), Is.EqualTo(-1));
			Assert.That(collection.FindIndex(1, 1, item => item == "BB"), Is.EqualTo(1));
			Assert.That(collection.FindIndex(2, 1, item => item == "BB"), Is.EqualTo(-1));
			Assert.That(collection.FindIndex(1, 3, item => item == "CC"), Is.EqualTo(3));
			Assert.That(collection.FindIndex(2, 2, item => item == null), Is.EqualTo(2));
			Assert.That(collection.FindIndex(0, 4, item => item == "DD"), Is.EqualTo(-1));
		}

		[Test]
		public void Test_FindIndex_StartIndex_Count_NullMatch()
		{
			var collection = new ViewModelCollection<String>(new[] { "AA", "BB", "CC" });

			Assert.Throws<ArgumentNullException>(() =>
				{
					collection.FindIndex(0, 1, null);
				});
		}

		[Test]
		public void Test_FindIndex_StartIndex_Count_InvalidStartIndex()
		{
			var collection = new ViewModelCollection<String>(new[] { "AA", "BB", "CC" });

			Assert.Throws<ArgumentOutOfRangeException>(() =>
				{
					collection.FindIndex(-1, 2, item => true);
				});
			Assert.Throws<ArgumentOutOfRangeException>(() =>
				{
					collection.FindIndex(4, 0, item => true);
				});
		}

		[Test]
		public void Test_FindIndex_StartIndex_Count_InvalidCount()
		{
			var collection = new ViewModelCollection<String>(new[] { "AA", "BB", "CC" });

			Assert.Throws<ArgumentOutOfRangeException>(() =>
				{
					collection.FindIndex(0, 4, item => true);
				});
			Assert.Throws<ArgumentOutOfRangeException>(() =>
				{
					collection.FindIndex(3, 1, item => true);
				});
		}


		[Test]
		public void Test_FindLastIndex()
		{
			var collection = new ViewModelCollection<String>(new[] { 
				"AA", "BB", null, "CC", "AA" });

			Assert.That(collection.FindLastIndex(item => item == "AA"), Is.EqualTo(4));
			Assert.That(collection.FindLastIndex(item => item == "BB"), Is.EqualTo(1));
			Assert.That(collection.FindLastIndex(item => item == "CC"), Is.EqualTo(3));
			Assert.That(collection.FindLastIndex(item => item == null), Is.EqualTo(2));
			Assert.That(collection.FindLastIndex(item => item == "DD"), Is.EqualTo(-1));
		}

		[Test]
		public void Test_FindLastIndex_NullMatch()
		{
			var collection = new ViewModelCollection<String>();

			Assert.Throws<ArgumentNullException>(() =>
				{
					collection.FindLastIndex(null);
				});
		}

		[Test]
		public void Test_FindLastIndex_StartIndex()
		{
			var collection = new ViewModelCollection<String>(new[] { 
				"AA", "BB", null, "CC", "AA" });

			Assert.That(collection.FindLastIndex(0, item => item == "AA"), Is.EqualTo(0));
			Assert.That(collection.FindLastIndex(4, item => item == "AA"), Is.EqualTo(4));
			Assert.That(collection.FindLastIndex(0, item => item == "BB"), Is.EqualTo(-1));
			Assert.That(collection.FindLastIndex(1, item => item == "BB"), Is.EqualTo(1));
			Assert.That(collection.FindLastIndex(2, item => item == "BB"), Is.EqualTo(1));
			Assert.That(collection.FindLastIndex(1, item => item == "CC"), Is.EqualTo(-1));
			Assert.That(collection.FindLastIndex(4, item => item == null), Is.EqualTo(2));
			Assert.That(collection.FindLastIndex(4, item => item == "DD"), Is.EqualTo(-1));
		}

		[Test]
		public void Test_FindLastIndex_StartLastIndex_NullMatch()
		{
			var collection = new ViewModelCollection<String>();

			Assert.Throws<ArgumentNullException>(() =>
				{
					collection.FindLastIndex(0, null);
				});
		}

		[Test]
		public void Test_FindLastIndex_StartLastIndex_InvalidStartLastIndex()
		{
			var collection = new ViewModelCollection<String>(new[] { "AA", "BB", "CC" });

			Assert.Throws<ArgumentOutOfRangeException>(() =>
				{
					collection.FindLastIndex(-1, item => true);
				});
			Assert.Throws<ArgumentOutOfRangeException>(() =>
				{
					collection.FindLastIndex(3, item => true);
				});
		}

		[Test]
		public void Test_FindLastIndex_StartIndex_Count()
		{
			var collection = new ViewModelCollection<String>(new[] { 
				"AA", "BB", null, "CC", "AA" });

			Assert.That(collection.FindLastIndex(0, 1, item => item == "AA"), Is.EqualTo(0));
			Assert.That(collection.FindLastIndex(3, 4, item => item == "AA"), Is.EqualTo(0));
			Assert.That(collection.FindLastIndex(4, 5, item => item == "AA"), Is.EqualTo(4));
			Assert.That(collection.FindLastIndex(3, 2, item => item == null), Is.EqualTo(2));
			Assert.That(collection.FindLastIndex(1, 2, item => item == null), Is.EqualTo(-1));
			Assert.That(collection.FindLastIndex(4, 4, item => item == "DD"), Is.EqualTo(-1));
		}

		[Test]
		public void Test_FindLastIndex_StartIndex_Count_NullMatch()
		{
			var collection = new ViewModelCollection<String>(new[] { "AA", "BB", "CC" });

			Assert.Throws<ArgumentNullException>(() =>
				{
					collection.FindLastIndex(2, 1, null);
				});
		}

		[Test]
		public void Test_FindLastIndex_StartIndex_Count_InvalidStartLastIndex()
		{
			var collection = new ViewModelCollection<String>(new[] { "AA", "BB", "CC" });

			Assert.Throws<ArgumentOutOfRangeException>(() =>
				{
					collection.FindLastIndex(-1, 2, item => true);
				});
			Assert.Throws<ArgumentOutOfRangeException>(() =>
				{
					collection.FindLastIndex(4, 1, item => true);
				});
		}

		[Test]
		public void Test_FindLastIndex_StartIndex_Count_InvalidCount()
		{
			var collection = new ViewModelCollection<String>(new[] { "AA", "BB", "CC" });

			Assert.Throws<ArgumentOutOfRangeException>(() =>
				{
					collection.FindLastIndex(0, 2, item => true);
				});
			Assert.Throws<ArgumentOutOfRangeException>(() =>
				{
					collection.FindLastIndex(3, 4, item => true);
				});
		}

		#endregion

		#region ++ Public Interface (ForEach) ++

		[Test]
		public void Test_ForEach()
		{
			var collection = new ViewModelCollection<String>(new[] { 
				"AA", "BB", "CC" });

			var items = new List<String>();
			collection.ForEach(item => items.Add(item));

			Assert.That(items, Is.EquivalentTo(new[] { "AA", "BB", "CC" }));
		}

		[Test]
		public void Test_ForEach_NullAction()
		{
			var collection = new ViewModelCollection<String>();

			Assert.Throws<ArgumentNullException>(() =>
				{
					collection.ForEach(null);
				});
		}

		#endregion

		#region ++ Public Interface (GetRange) ++

		[Test]
		public void Test_GetRange()
		{
			var collection = new ViewModelCollection<String>(new[] { 
				"AA", "BB", null, "CC" });

			Assert.That(collection.GetRange(0, 0), Is.Empty);
			Assert.That(collection.GetRange(0, 1), Is.EquivalentTo(new[] { "AA" }));
			Assert.That(collection.GetRange(0, 2), Is.EquivalentTo(new[] { "AA", "BB" }));
			Assert.That(collection.GetRange(0, 4), Is.EquivalentTo(new[] { "AA", "BB", null, "CC" }));
			Assert.That(collection.GetRange(1, 3), Is.EquivalentTo(new[] { "BB", null, "CC" }));
			Assert.That(collection.GetRange(3, 1), Is.EquivalentTo(new[] { "CC" }));
			Assert.That(collection.GetRange(2, 0), Is.Empty);
		}

		[Test]
		public void Test_GetRange_Index_Count_InvalidIndex()
		{
			var collection = new ViewModelCollection<String>(new[] { "AA", "BB", "CC" });

			Assert.Throws<ArgumentOutOfRangeException>(() =>
				{
					collection.GetRange(-1, 2);
				});
			Assert.Throws<ArgumentException>(() =>
				{
					collection.GetRange(4, 0);
				});
		}

		[Test]
		public void Test_GetRange_Index_Count_InvalidCount()
		{
			var collection = new ViewModelCollection<String>(new[] { "AA", "BB", "CC" });

			Assert.Throws<ArgumentException>(() =>
				{
					collection.GetRange(0, 4);
				});
			Assert.Throws<ArgumentException>(() =>
				{
					collection.GetRange(3, 1);
				});
		}

		#endregion

		#region ++ Public Interface (IndexOf, LastIndexOf) ++

		[Test]
		public void Test_IndexOf()
		{
			var collection = new ViewModelCollection<String>(new[] { 
				"AA", "BB", null, "CC", "AA" });

			Assert.That(collection.IndexOf("AA"), Is.EqualTo(0));
			Assert.That(collection.IndexOf("BB"), Is.EqualTo(1));
			Assert.That(collection.IndexOf("CC"), Is.EqualTo(3));
			Assert.That(collection.IndexOf(null), Is.EqualTo(2));
			Assert.That(collection.IndexOf("DD"), Is.EqualTo(-1));
		}

		[Test]
		public void Test_IndexOf_Index()
		{
			var collection = new ViewModelCollection<String>(new[] { 
				"AA", "BB", null, "CC", "AA" });

			Assert.That(collection.IndexOf("AA", 0), Is.EqualTo(0));
			Assert.That(collection.IndexOf("AA", 1), Is.EqualTo(4));
			Assert.That(collection.IndexOf("BB", 0), Is.EqualTo(1));
			Assert.That(collection.IndexOf("BB", 1), Is.EqualTo(1));
			Assert.That(collection.IndexOf("BB", 2), Is.EqualTo(-1));
			Assert.That(collection.IndexOf("CC", 2), Is.EqualTo(3));
			Assert.That(collection.IndexOf(null, 2), Is.EqualTo(2));
			Assert.That(collection.IndexOf("DD", 0), Is.EqualTo(-1));
		}

		[Test]
		public void Test_IndexOf_Index_InvalidIndex()
		{
			var collection = new ViewModelCollection<String>(new[] { 
				"AA", "BB", "CC" });

			Assert.Throws<ArgumentOutOfRangeException>(() =>
				{
					collection.IndexOf("AA", -1);
				});
			Assert.Throws<ArgumentOutOfRangeException>(() =>
				{
					collection.IndexOf("AA", 4);
				});
		}

		[Test]
		public void Test_IndexOf_Index_Count()
		{
			var collection = new ViewModelCollection<String>(new[] { 
				"AA", "BB", null, "CC", "AA" });

			Assert.That(collection.IndexOf("AA", 0, 0), Is.EqualTo(-1));
			Assert.That(collection.IndexOf("AA", 0, 1), Is.EqualTo(0));
			Assert.That(collection.IndexOf("AA", 1, 4), Is.EqualTo(4));
			Assert.That(collection.IndexOf("BB", 0, 2), Is.EqualTo(1));
			Assert.That(collection.IndexOf("BB", 1, 1), Is.EqualTo(1));
			Assert.That(collection.IndexOf("BB", 2, 2), Is.EqualTo(-1));
			Assert.That(collection.IndexOf("CC", 2, 2), Is.EqualTo(3));
			Assert.That(collection.IndexOf(null, 2, 1), Is.EqualTo(2));
			Assert.That(collection.IndexOf("DD", 0, 4), Is.EqualTo(-1));
		}

		[Test]
		public void Test_IndexOf_Index_Count_InvalidIndex()
		{
			var collection = new ViewModelCollection<String>(new[] { 
				"AA", "BB", "CC" });

			Assert.Throws<ArgumentOutOfRangeException>(() =>
				{
					collection.IndexOf("AA", -1, 1);
				});
			Assert.Throws<ArgumentOutOfRangeException>(() =>
				{
					collection.IndexOf("AA", 4, 0);
				});
		}

		[Test]
		public void Test_IndexOf_Index_Count_InvalidCount()
		{
			var collection = new ViewModelCollection<String>(new[] { 
				"AA", "BB", "CC" });

			Assert.Throws<ArgumentOutOfRangeException>(() =>
				{
					collection.IndexOf("AA", 0, 4);
				});
			Assert.Throws<ArgumentOutOfRangeException>(() =>
				{
					collection.IndexOf("AA", 3, 2);
				});
		}


		[Test]
		public void Test_LastIndexOf()
		{
			var collection = new ViewModelCollection<String>(new[] { 
				"AA", "BB", null, "CC", "AA" });

			Assert.That(collection.LastIndexOf("AA"), Is.EqualTo(4));
			Assert.That(collection.LastIndexOf("BB"), Is.EqualTo(1));
			Assert.That(collection.LastIndexOf("CC"), Is.EqualTo(3));
			Assert.That(collection.LastIndexOf(null), Is.EqualTo(2));
			Assert.That(collection.LastIndexOf("DD"), Is.EqualTo(-1));
		}

		[Test]
		public void Test_LastIndexOf_Index()
		{
			var collection = new ViewModelCollection<String>(new[] { 
				"AA", "BB", null, "CC", "AA" });

			Assert.That(collection.LastIndexOf("AA", 0), Is.EqualTo(0));
			Assert.That(collection.LastIndexOf("AA", 4), Is.EqualTo(4));
			Assert.That(collection.LastIndexOf("BB", 2), Is.EqualTo(1));
			Assert.That(collection.LastIndexOf("BB", 1), Is.EqualTo(1));
			Assert.That(collection.LastIndexOf("BB", 0), Is.EqualTo(-1));
			Assert.That(collection.LastIndexOf("CC", 4), Is.EqualTo(3));
			Assert.That(collection.LastIndexOf(null, 3), Is.EqualTo(2));
			Assert.That(collection.LastIndexOf("DD", 3), Is.EqualTo(-1));
		}

		[Test]
		public void Test_LastIndexOf_Index_InvalidIndex()
		{
			var collection = new ViewModelCollection<String>(new[] { 
				"AA", "BB", "CC" });

			Assert.Throws<ArgumentOutOfRangeException>(() =>
				{
					collection.LastIndexOf("AA", -1);
				});
			Assert.Throws<ArgumentOutOfRangeException>(() =>
				{
					collection.LastIndexOf("AA", 4);
				});
		}

		[Test]
		public void Test_LastIndexOf_Index_Count()
		{
			var collection = new ViewModelCollection<String>(new[] { 
				"AA", "BB", null, "CC", "AA" });

			Assert.That(collection.LastIndexOf("AA", 4, 0), Is.EqualTo(-1));
			Assert.That(collection.LastIndexOf("AA", 0, 1), Is.EqualTo(0));
			Assert.That(collection.LastIndexOf("AA", 4, 1), Is.EqualTo(4));
			Assert.That(collection.LastIndexOf("BB", 2, 2), Is.EqualTo(1));
			Assert.That(collection.LastIndexOf("BB", 2, 1), Is.EqualTo(-1));
			Assert.That(collection.LastIndexOf("BB", 2, 0), Is.EqualTo(-1));
			Assert.That(collection.LastIndexOf("CC", 4, 2), Is.EqualTo(3));
			Assert.That(collection.LastIndexOf(null, 3, 2), Is.EqualTo(2));
			Assert.That(collection.LastIndexOf("DD", 4, 4), Is.EqualTo(-1));
		}

		[Test]
		public void Test_LastIndexOf_Index_Count_InvalidIndex()
		{
			var collection = new ViewModelCollection<String>(new[] { 
				"AA", "BB", "CC" });

			Assert.Throws<ArgumentOutOfRangeException>(() =>
				{
					collection.LastIndexOf("AA", -1, 1);
				});
			Assert.Throws<ArgumentOutOfRangeException>(() =>
				{
					collection.LastIndexOf("AA", 4, 1);
				});
		}

		[Test]
		public void Test_LastIndexOf_Index_Count_InvalidCount()
		{
			var collection = new ViewModelCollection<String>(new[] { 
				"AA", "BB", "CC" });

			Assert.Throws<ArgumentOutOfRangeException>(() =>
				{
					collection.LastIndexOf("AA", 3, 4);
				});
			Assert.Throws<ArgumentOutOfRangeException>(() =>
				{
					collection.LastIndexOf("AA", 0, 2);
				});
		}

		#endregion

		#region ++ Public Interface (Insert, InsertRange) ++

		[Test]
		public void Test_Insert()
		{
			var collection = new ViewModelCollection<String>();
			_Subscribe(collection);

			// initial state
			Assert.That(collection.Count, Is.EqualTo(0));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(0));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(0));

			// first item inserted
			collection.Insert(0, "AA");

			Assert.That(collection.Count, Is.EqualTo(1));
			Assert.That(collection[0], Is.EqualTo("AA"));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(3));
			Assert.That(mPropertyChanges.AsString(";"), Is.EqualTo("Count;Item[];HasItems"));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(1));
			Assert.That(mCollectionChanges[0].Action, Is.EqualTo(NotifyCollectionChangedAction.Add));
			Assert.That(mCollectionChanges[0].NewItems, Is.EquivalentTo(new[] { "AA" }));
			Assert.That(mCollectionChanges[0].NewStartingIndex, Is.EqualTo(0));
			Assert.That(mCollectionChanges[0].OldItems, Is.Null);
			Assert.That(mCollectionChanges[0].OldStartingIndex, Is.EqualTo(-1));

			// second item inserted
			_Reset();
			collection.Insert(0, null);

			Assert.That(collection.Count, Is.EqualTo(2));
			Assert.That(collection[0], Is.EqualTo(null));
			Assert.That(collection[1], Is.EqualTo("AA"));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(3));
			Assert.That(mPropertyChanges.AsString(";"), Is.EqualTo("Count;Item[];HasItems"));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(1));
			Assert.That(mCollectionChanges[0].Action, Is.EqualTo(NotifyCollectionChangedAction.Add));
			Assert.That(mCollectionChanges[0].NewItems, Is.EquivalentTo(new String[] { null }));
			Assert.That(mCollectionChanges[0].NewStartingIndex, Is.EqualTo(0));
			Assert.That(mCollectionChanges[0].OldItems, Is.Null);
			Assert.That(mCollectionChanges[0].OldStartingIndex, Is.EqualTo(-1));

			// third item inserted
			_Reset();
			collection.Insert(2, "CC");

			Assert.That(collection.Count, Is.EqualTo(3));
			Assert.That(collection[0], Is.EqualTo(null));
			Assert.That(collection[1], Is.EqualTo("AA"));
			Assert.That(collection[2], Is.EqualTo("CC"));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(3));
			Assert.That(mPropertyChanges.AsString(";"), Is.EqualTo("Count;Item[];HasItems"));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(1));
			Assert.That(mCollectionChanges[0].Action, Is.EqualTo(NotifyCollectionChangedAction.Add));
			Assert.That(mCollectionChanges[0].NewItems, Is.EquivalentTo(new[] { "CC" }));
			Assert.That(mCollectionChanges[0].NewStartingIndex, Is.EqualTo(2));
			Assert.That(mCollectionChanges[0].OldItems, Is.Null);
			Assert.That(mCollectionChanges[0].OldStartingIndex, Is.EqualTo(-1));
		}

		[Test]
		public void Test_Insert_Duplicates()
		{
			var collection = new ViewModelCollection<String>();
			_Subscribe(collection);

			// initial state
			Assert.That(collection.Count, Is.EqualTo(0));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(0));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(0));

			// item inserted twice
			collection.Insert(0, "AA");
			collection.Insert(1, "AA");

			Assert.That(collection.Count, Is.EqualTo(2));
			Assert.That(collection[0], Is.EqualTo("AA"));
			Assert.That(collection[1], Is.EqualTo("AA"));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(6));
			Assert.That(mPropertyChanges.AsString(";"), Is.EqualTo("Count;Item[];HasItems;Count;Item[];HasItems"));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(2));
			Assert.That(mCollectionChanges[0].Action, Is.EqualTo(NotifyCollectionChangedAction.Add));
			Assert.That(mCollectionChanges[0].NewItems, Is.EquivalentTo(new String[] { "AA" }));
			Assert.That(mCollectionChanges[0].NewStartingIndex, Is.EqualTo(0));
			Assert.That(mCollectionChanges[0].OldItems, Is.Null);
			Assert.That(mCollectionChanges[0].OldStartingIndex, Is.EqualTo(-1));
			Assert.That(mCollectionChanges[1].Action, Is.EqualTo(NotifyCollectionChangedAction.Add));
			Assert.That(mCollectionChanges[1].NewItems, Is.EquivalentTo(new String[] { "AA" }));
			Assert.That(mCollectionChanges[1].NewStartingIndex, Is.EqualTo(1));
			Assert.That(mCollectionChanges[1].OldItems, Is.Null);
			Assert.That(mCollectionChanges[1].OldStartingIndex, Is.EqualTo(-1));
		}

		[Test]
		public void Test_Insert_Reentrancy()
		{
			var collection = new ViewModelCollection<String>();

			collection.CollectionChanged += (sender, e) =>
				{
					// not allowed
					collection.Insert(0, "TEST");
				};

			Assert.Throws<InvalidOperationException>(() =>
				{
					collection.Insert(0, "AA");
				});
		}


		[Test]
		public void Test_InsertRange()
		{
			var collection = new ViewModelCollection<String>();
			_Subscribe(collection);

			// initial state
			Assert.That(collection.Count, Is.EqualTo(0));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(0));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(0));

			// two items inserted
			collection.InsertRange(0, new[] { "AA", "BB" });

			Assert.That(collection.Count, Is.EqualTo(2));
			Assert.That(collection[0], Is.EqualTo("AA"));
			Assert.That(collection[1], Is.EqualTo("BB"));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(3));
			Assert.That(mPropertyChanges.AsString(";"), Is.EqualTo("Count;Item[];HasItems"));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(1));
			Assert.That(mCollectionChanges[0].Action, Is.EqualTo(NotifyCollectionChangedAction.Add));
			Assert.That(mCollectionChanges[0].NewItems, Is.EquivalentTo(new[] { "AA", "BB" }));
			Assert.That(mCollectionChanges[0].NewStartingIndex, Is.EqualTo(0));
			Assert.That(mCollectionChanges[0].OldItems, Is.Null);
			Assert.That(mCollectionChanges[0].OldStartingIndex, Is.EqualTo(-1));

			// three items inserted
			_Reset();
			collection.InsertRange(1, new[] { "CC", null, "DD" });

			Assert.That(collection.Count, Is.EqualTo(5));
			Assert.That(collection[0], Is.EqualTo("AA"));
			Assert.That(collection[1], Is.EqualTo("CC"));
			Assert.That(collection[2], Is.EqualTo(null));
			Assert.That(collection[3], Is.EqualTo("DD"));
			Assert.That(collection[4], Is.EqualTo("BB"));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(3));
			Assert.That(mPropertyChanges.AsString(";"), Is.EqualTo("Count;Item[];HasItems"));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(1));
			Assert.That(mCollectionChanges[0].Action, Is.EqualTo(NotifyCollectionChangedAction.Add));
			Assert.That(mCollectionChanges[0].NewItems, Is.EquivalentTo(new[] { "CC", null, "DD" }));
			Assert.That(mCollectionChanges[0].NewStartingIndex, Is.EqualTo(1));
			Assert.That(mCollectionChanges[0].OldItems, Is.Null);
			Assert.That(mCollectionChanges[0].OldStartingIndex, Is.EqualTo(-1));
		}

		[Test]
		public void Test_InsertRange_NonListCollection()
		{
			var collection = new ViewModelCollection<String>();
			_Subscribe(collection);

			// initial state
			Assert.That(collection.Count, Is.EqualTo(0));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(0));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(0));

			// two items added
			var set = new HashSet<String>(new[] { "AA", "BB" });
			collection.InsertRange(0, set);

			Assert.That(collection.Count, Is.EqualTo(2));
			Assert.That(collection[0], Is.EqualTo("AA"));
			Assert.That(collection[1], Is.EqualTo("BB"));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(3));
			Assert.That(mPropertyChanges.AsString(";"), Is.EqualTo("Count;Item[];HasItems"));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(1));
			Assert.That(mCollectionChanges[0].Action, Is.EqualTo(NotifyCollectionChangedAction.Add));
			Assert.That(mCollectionChanges[0].NewItems, Is.EquivalentTo(new[] { "AA", "BB" }));
			Assert.That(mCollectionChanges[0].NewStartingIndex, Is.EqualTo(0));
			Assert.That(mCollectionChanges[0].OldItems, Is.Null);
			Assert.That(mCollectionChanges[0].OldStartingIndex, Is.EqualTo(-1));
		}

		[Test]
		public void Test_InsertRange_EmptyCollection()
		{
			var collection = new ViewModelCollection<String>();
			_Subscribe(collection);

			// initial state
			Assert.That(collection.Count, Is.EqualTo(0));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(0));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(0));

			// empty collection added
			collection.InsertRange(0, new String[0]);

			Assert.That(collection.Count, Is.EqualTo(0));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(0));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(0));
		}

		[Test]
		public void Test_InsertRange_IndexOutOfRange_BelowZero()
		{
			var collection = new ViewModelCollection<String>(new[] { "AA", "BB", "CC" });

			Assert.Throws<ArgumentOutOfRangeException>(() =>
				{
					collection.InsertRange(-1, new[] { "DD" });
				});
		}

		[Test]
		public void Test_InsertRange_IndexOutOfRange_AboveCount()
		{
			var collection = new ViewModelCollection<String>(new[] { "AA", "BB", "CC" });

			Assert.Throws<ArgumentOutOfRangeException>(() =>
				{
					collection.InsertRange(4, new[] { "DD" });
				});
		}

		[Test]
		public void Test_InsertRange_Null()
		{
			var collection = new ViewModelCollection<String>(new[] { "AA", "BB", "CC" });

			Assert.Throws<ArgumentNullException>(() =>
				{
					collection.InsertRange(1, null);
				});
		}

		[Test]
		public void Test_InsertRange_Reentrancy()
		{
			var collection = new ViewModelCollection<String>();

			collection.CollectionChanged += (sender, e) =>
				{
					// not allowed
					collection.Add("TEST");
				};

			Assert.Throws<InvalidOperationException>(() =>
				{
					collection.InsertRange(0, new[] { "AA", "BB" });
				});
		}

		#endregion

		#region ++ Public Interface (Remove, RemoveAt) ++

		[Test]
		public void Test_Remove()
		{
			var collection = new ViewModelCollection<String>(new[] { "AA", "BB", "CC" });
			_Subscribe(collection);

			// initial state
			Assert.That(collection.Count, Is.EqualTo(3));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(0));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(0));

			// item removed
			Assert.That(collection.Remove("BB"), Is.True);

			Assert.That(collection.Count, Is.EqualTo(2));
			Assert.That(collection[0], Is.EqualTo("AA"));
			Assert.That(collection[1], Is.EqualTo("CC"));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(3));
			Assert.That(mPropertyChanges.AsString(";"), Is.EqualTo("Count;Item[];HasItems"));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(1));
			Assert.That(mCollectionChanges[0].Action, Is.EqualTo(NotifyCollectionChangedAction.Remove));
			Assert.That(mCollectionChanges[0].NewItems, Is.Null);
			Assert.That(mCollectionChanges[0].NewStartingIndex, Is.EqualTo(-1));
			Assert.That(mCollectionChanges[0].OldItems, Is.EquivalentTo(new[] { "BB" }));
			Assert.That(mCollectionChanges[0].OldStartingIndex, Is.EqualTo(1));

			// item removed
			_Reset();
			Assert.That(collection.Remove("AA"), Is.True);

			Assert.That(collection.Count, Is.EqualTo(1));
			Assert.That(collection[0], Is.EqualTo("CC"));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(3));
			Assert.That(mPropertyChanges.AsString(";"), Is.EqualTo("Count;Item[];HasItems"));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(1));
			Assert.That(mCollectionChanges[0].Action, Is.EqualTo(NotifyCollectionChangedAction.Remove));
			Assert.That(mCollectionChanges[0].NewItems, Is.Null);
			Assert.That(mCollectionChanges[0].NewStartingIndex, Is.EqualTo(-1));
			Assert.That(mCollectionChanges[0].OldItems, Is.EquivalentTo(new String[] { "AA" }));
			Assert.That(mCollectionChanges[0].OldStartingIndex, Is.EqualTo(0));
		}

		[Test]
		public void Test_Remove_NonExistingItem()
		{
			var collection = new ViewModelCollection<String>(new[] { "AA", "BB", "CC" });
			_Subscribe(collection);

			// initial state
			Assert.That(collection.Count, Is.EqualTo(3));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(0));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(0));

			// item removed
			Assert.That(collection.Remove("DD"), Is.False);

			Assert.That(collection.Count, Is.EqualTo(3));
			Assert.That(collection[0], Is.EqualTo("AA"));
			Assert.That(collection[1], Is.EqualTo("BB"));
			Assert.That(collection[2], Is.EqualTo("CC"));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(0));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(0));
		}

		[Test]
		public void Test_Remove_Null()
		{
			var collection = new ViewModelCollection<String>(new[] { "AA", null, "CC" });
			_Subscribe(collection);

			// initial state
			Assert.That(collection.Count, Is.EqualTo(3));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(0));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(0));

			// item removed
			Assert.That(collection.Remove(null), Is.True);

			Assert.That(collection.Count, Is.EqualTo(2));
			Assert.That(collection[0], Is.EqualTo("AA"));
			Assert.That(collection[1], Is.EqualTo("CC"));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(3));
			Assert.That(mPropertyChanges.AsString(";"), Is.EqualTo("Count;Item[];HasItems"));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(1));
			Assert.That(mCollectionChanges[0].Action, Is.EqualTo(NotifyCollectionChangedAction.Remove));
			Assert.That(mCollectionChanges[0].NewItems, Is.Null);
			Assert.That(mCollectionChanges[0].NewStartingIndex, Is.EqualTo(-1));
			Assert.That(mCollectionChanges[0].OldItems, Is.EquivalentTo(new String[] { null }));
			Assert.That(mCollectionChanges[0].OldStartingIndex, Is.EqualTo(1));
		}

		[Test]
		public void Test_Remove_Reentrancy()
		{
			var collection = new ViewModelCollection<String>(new[] { "AA", "BB" });

			collection.CollectionChanged += (sender, e) =>
				{
					// not allowed
					collection.Remove("BB");
				};

			Assert.Throws<InvalidOperationException>(() =>
				{
					collection.Remove("AA");
				});
		}


		[Test]
		public void Test_RemoveAll()
		{
			var collection = new ViewModelCollection<String>(new[] { "AA", "BB", "CC", "AA", "BB" });
			_Subscribe(collection);

			// initial state
			Assert.That(collection.Count, Is.EqualTo(5));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(0));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(0));

			// items removed
			Assert.That(collection.RemoveAll(item => item == "AA"), Is.EqualTo(2));

			Assert.That(collection.Count, Is.EqualTo(3));
			Assert.That(collection[0], Is.EqualTo("BB"));
			Assert.That(collection[1], Is.EqualTo("CC"));
			Assert.That(collection[2], Is.EqualTo("BB"));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(3));
			Assert.That(mPropertyChanges.AsString(";"), Is.EqualTo("Count;Item[];HasItems"));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(1));
			Assert.That(mCollectionChanges[0].Action, Is.EqualTo(NotifyCollectionChangedAction.Reset));
			Assert.That(mCollectionChanges[0].NewItems, Is.Null);
			Assert.That(mCollectionChanges[0].NewStartingIndex, Is.EqualTo(-1));
			Assert.That(mCollectionChanges[0].OldItems, Is.Null);
			Assert.That(mCollectionChanges[0].OldStartingIndex, Is.EqualTo(-1));

			// items removed
			_Reset();
			Assert.That(collection.RemoveAll(item => item == "BB"), Is.EqualTo(2));

			Assert.That(collection.Count, Is.EqualTo(1));
			Assert.That(collection[0], Is.EqualTo("CC"));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(3));
			Assert.That(mPropertyChanges.AsString(";"), Is.EqualTo("Count;Item[];HasItems"));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(1));
			Assert.That(mCollectionChanges[0].Action, Is.EqualTo(NotifyCollectionChangedAction.Reset));
			Assert.That(mCollectionChanges[0].NewItems, Is.Null);
			Assert.That(mCollectionChanges[0].NewStartingIndex, Is.EqualTo(-1));
			Assert.That(mCollectionChanges[0].OldItems, Is.Null);
			Assert.That(mCollectionChanges[0].OldStartingIndex, Is.EqualTo(-1));
		}

		[Test]
		public void Test_RemoveAll_NotExisting()
		{
			var collection = new ViewModelCollection<String>(new[] { "AA", "BB", "CC" });
			_Subscribe(collection);

			Assert.That(collection.RemoveAll(item => item == "DD"), Is.EqualTo(0));

			Assert.That(collection.Count, Is.EqualTo(3));
			Assert.That(collection[0], Is.EqualTo("AA"));
			Assert.That(collection[1], Is.EqualTo("BB"));
			Assert.That(collection[2], Is.EqualTo("CC"));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(0));
		}

		[Test]
		public void Test_RemoveAll_Null()
		{
			var collection = new ViewModelCollection<String>(new[] { "AA", "BB", "CC" });

			Assert.Throws<ArgumentNullException>(() =>
				{
					collection.RemoveAll(null);
				});
		}

		[Test]
		public void Test_RemoveAll_Reentrancy()
		{
			var collection = new ViewModelCollection<String>(new[] { "AA", "BB" });

			collection.CollectionChanged += (sender, e) =>
				{
					// not allowed
					collection.RemoveAll(item => true);
				};

			Assert.Throws<InvalidOperationException>(() =>
				{
					collection.RemoveAll(item => true);
				});
		}


		[Test]
		public void Test_RemoveAt()
		{
			var collection = new ViewModelCollection<String>(new[] { "AA", "BB", "CC" });
			_Subscribe(collection);

			// initial state
			Assert.That(collection.Count, Is.EqualTo(3));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(0));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(0));

			// item removed
			collection.RemoveAt(1);

			Assert.That(collection.Count, Is.EqualTo(2));
			Assert.That(collection[0], Is.EqualTo("AA"));
			Assert.That(collection[1], Is.EqualTo("CC"));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(3));
			Assert.That(mPropertyChanges.AsString(";"), Is.EqualTo("Count;Item[];HasItems"));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(1));
			Assert.That(mCollectionChanges[0].Action, Is.EqualTo(NotifyCollectionChangedAction.Remove));
			Assert.That(mCollectionChanges[0].NewItems, Is.Null);
			Assert.That(mCollectionChanges[0].NewStartingIndex, Is.EqualTo(-1));
			Assert.That(mCollectionChanges[0].OldItems, Is.EquivalentTo(new[] { "BB" }));
			Assert.That(mCollectionChanges[0].OldStartingIndex, Is.EqualTo(1));

			// item removed
			_Reset();
			collection.RemoveAt(0);

			Assert.That(collection.Count, Is.EqualTo(1));
			Assert.That(collection[0], Is.EqualTo("CC"));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(3));
			Assert.That(mPropertyChanges.AsString(";"), Is.EqualTo("Count;Item[];HasItems"));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(1));
			Assert.That(mCollectionChanges[0].Action, Is.EqualTo(NotifyCollectionChangedAction.Remove));
			Assert.That(mCollectionChanges[0].NewItems, Is.Null);
			Assert.That(mCollectionChanges[0].NewStartingIndex, Is.EqualTo(-1));
			Assert.That(mCollectionChanges[0].OldItems, Is.EquivalentTo(new String[] { "AA" }));
			Assert.That(mCollectionChanges[0].OldStartingIndex, Is.EqualTo(0));
		}

		[Test]
		public void Test_RemoveAt_IndexOutOfRange_BelowZero()
		{
			var collection = new ViewModelCollection<String>(new[] { "AA", "BB", "CC" });

			Assert.Throws<ArgumentOutOfRangeException>(() =>
				{
					collection.RemoveAt(-1);
				});

		}

		[Test]
		public void Test_RemoveAt_IndexOutOfRange_AboveCount()
		{
			var collection = new ViewModelCollection<String>(new[] { "AA", "BB", "CC" });

			Assert.Throws<ArgumentOutOfRangeException>(() =>
				{
					collection.RemoveAt(3);
				});
		}

		[Test]
		public void Test_RemoveAt_Reentrancy()
		{
			var collection = new ViewModelCollection<String>(new[] { "AA", "BB" });

			collection.CollectionChanged += (sender, e) =>
				{
					// not allowed
					collection.RemoveAt(0);
				};

			Assert.Throws<InvalidOperationException>(() =>
				{
					collection.RemoveAt(0);
				});
		}


		[Test]
		public void Test_RemoveRange()
		{
			var collection = new ViewModelCollection<String>(new[] { "AA", "BB", "CC" });
			_Subscribe(collection);

			// initial state
			Assert.That(collection.Count, Is.EqualTo(3));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(0));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(0));

			// items removed
			collection.RemoveRange(1, 2);

			Assert.That(collection.Count, Is.EqualTo(1));
			Assert.That(collection[0], Is.EqualTo("AA"));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(3));
			Assert.That(mPropertyChanges.AsString(";"), Is.EqualTo("Count;Item[];HasItems"));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(1));
			Assert.That(mCollectionChanges[0].Action, Is.EqualTo(NotifyCollectionChangedAction.Remove));
			Assert.That(mCollectionChanges[0].NewItems, Is.Null);
			Assert.That(mCollectionChanges[0].NewStartingIndex, Is.EqualTo(-1));
			Assert.That(mCollectionChanges[0].OldItems, Is.EquivalentTo(new[] { "BB", "CC" }));
			Assert.That(mCollectionChanges[0].OldStartingIndex, Is.EqualTo(1));

			// items removed
			_Reset();
			collection.RemoveRange(0, 1);

			Assert.That(collection.Count, Is.EqualTo(0));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(3));
			Assert.That(mPropertyChanges.AsString(";"), Is.EqualTo("Count;Item[];HasItems"));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(1));
			Assert.That(mCollectionChanges[0].Action, Is.EqualTo(NotifyCollectionChangedAction.Remove));
			Assert.That(mCollectionChanges[0].NewItems, Is.Null);
			Assert.That(mCollectionChanges[0].NewStartingIndex, Is.EqualTo(-1));
			Assert.That(mCollectionChanges[0].OldItems, Is.EquivalentTo(new String[] { "AA" }));
			Assert.That(mCollectionChanges[0].OldStartingIndex, Is.EqualTo(0));
		}

		[Test]
		public void Test_RemoveRange_AllAtOnce()
		{
			var collection = new ViewModelCollection<String>(new[] { "AA", "BB", "CC" });
			_Subscribe(collection);

			// initial state
			Assert.That(collection.Count, Is.EqualTo(3));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(0));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(0));

			// items removed
			collection.RemoveRange(0, 3);

			Assert.That(collection.Count, Is.EqualTo(0));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(3));
			Assert.That(mPropertyChanges.AsString(";"), Is.EqualTo("Count;Item[];HasItems"));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(1));
			Assert.That(mCollectionChanges[0].Action, Is.EqualTo(NotifyCollectionChangedAction.Remove));
			Assert.That(mCollectionChanges[0].NewItems, Is.Null);
			Assert.That(mCollectionChanges[0].NewStartingIndex, Is.EqualTo(-1));
			Assert.That(mCollectionChanges[0].OldItems, Is.EquivalentTo(new[] { "AA", "BB", "CC" }));
			Assert.That(mCollectionChanges[0].OldStartingIndex, Is.EqualTo(0));
		}

		[Test]
		public void Test_RemoveRange_EmptyRange()
		{
			var collection = new ViewModelCollection<String>(new[] { "AA", "BB", "CC" });
			_Subscribe(collection);

			// initial state
			Assert.That(collection.Count, Is.EqualTo(3));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(0));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(0));

			// nothing removed
			collection.RemoveRange(1, 0);

			Assert.That(collection.Count, Is.EqualTo(3));
			Assert.That(collection[0], Is.EqualTo("AA"));
			Assert.That(collection[1], Is.EqualTo("BB"));
			Assert.That(collection[2], Is.EqualTo("CC"));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(0));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(0));
		}

		[Test]
		public void Test_RemoveRange_ArgumentOutOfRange()
		{
			var collection = new ViewModelCollection<String>(new[] { "AA", "BB", "CC" });

			Assert.Throws<ArgumentOutOfRangeException>(() =>
				{
					collection.RemoveRange(-1, 2);
				});

			Assert.DoesNotThrow(() =>
				{
					collection.RemoveRange(3, 0);
				});
			Assert.Throws<ArgumentOutOfRangeException>(() =>
				{
					collection.RemoveRange(3, 1);
				});
			Assert.Throws<ArgumentOutOfRangeException>(() =>
				{
					collection.RemoveRange(0, 4);
				});
			Assert.Throws<ArgumentOutOfRangeException>(() =>
				{
					collection.RemoveRange(2, 2);
				});
		}

		[Test]
		public void Test_RemoveRange_Reentrancy()
		{
			var collection = new ViewModelCollection<String>(new[] { "AA", "BB" });

			collection.CollectionChanged += (sender, e) =>
				{
					// not allowed
					collection.RemoveRange(0, 1);
				};

			Assert.Throws<InvalidOperationException>(() =>
				{
					collection.RemoveRange(0, 1);
				});
		}

		#endregion

		#region ++ Public Interface (Sort) ++

		[Test]
		public void Test_Sort()
		{
			var collection = new ViewModelCollection<String>(new[] { "BB", "AA", "CC" });
			_Subscribe(collection);

			// initial state
			Assert.That(collection.Count, Is.EqualTo(3));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(0));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(0));

			// item sorted
			collection.Sort();

			Assert.That(collection.Count, Is.EqualTo(3));
			Assert.That(collection[0], Is.EqualTo("AA"));
			Assert.That(collection[1], Is.EqualTo("BB"));
			Assert.That(collection[2], Is.EqualTo("CC"));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(0));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(1));
			Assert.That(mCollectionChanges[0].Action, Is.EqualTo(NotifyCollectionChangedAction.Reset));
			Assert.That(mCollectionChanges[0].NewItems, Is.Null);
			Assert.That(mCollectionChanges[0].NewStartingIndex, Is.EqualTo(-1));
			Assert.That(mCollectionChanges[0].OldItems, Is.Null);
			Assert.That(mCollectionChanges[0].OldStartingIndex, Is.EqualTo(-1));
		}


		[Test]
		public void Test_Sort_Comparison()
		{
			var collection = new ViewModelCollection<String>(new[] { "BB", "AA", "CC" });
			_Subscribe(collection);

			// initial state
			Assert.That(collection.Count, Is.EqualTo(3));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(0));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(0));

			// item sorted
			collection.Sort((x, y) => x.CompareTo(y));

			Assert.That(collection.Count, Is.EqualTo(3));
			Assert.That(collection[0], Is.EqualTo("AA"));
			Assert.That(collection[1], Is.EqualTo("BB"));
			Assert.That(collection[2], Is.EqualTo("CC"));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(0));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(1));
			Assert.That(mCollectionChanges[0].Action, Is.EqualTo(NotifyCollectionChangedAction.Reset));
			Assert.That(mCollectionChanges[0].NewItems, Is.Null);
			Assert.That(mCollectionChanges[0].NewStartingIndex, Is.EqualTo(-1));
			Assert.That(mCollectionChanges[0].OldItems, Is.Null);
			Assert.That(mCollectionChanges[0].OldStartingIndex, Is.EqualTo(-1));
		}

		[Test]
		public void Test_Sort_Comparison_Null()
		{
			var collection = new ViewModelCollection<String>(new[] { "BB", "AA", "CC" });

			Assert.Throws<ArgumentNullException>(() =>
				{
					collection.Sort((Comparison<String>)null);
				});
		}


		[Test]
		public void Test_Sort_Comparer()
		{
			var collection = new ViewModelCollection<String>(new[] { "BB", "AA", "CC" });
			_Subscribe(collection);

			// initial state
			Assert.That(collection.Count, Is.EqualTo(3));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(0));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(0));

			// item sorted
			collection.Sort(Comparer<String>.Default);

			Assert.That(collection.Count, Is.EqualTo(3));
			Assert.That(collection[0], Is.EqualTo("AA"));
			Assert.That(collection[1], Is.EqualTo("BB"));
			Assert.That(collection[2], Is.EqualTo("CC"));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(0));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(1));
			Assert.That(mCollectionChanges[0].Action, Is.EqualTo(NotifyCollectionChangedAction.Reset));
			Assert.That(mCollectionChanges[0].NewItems, Is.Null);
			Assert.That(mCollectionChanges[0].NewStartingIndex, Is.EqualTo(-1));
			Assert.That(mCollectionChanges[0].OldItems, Is.Null);
			Assert.That(mCollectionChanges[0].OldStartingIndex, Is.EqualTo(-1));
		}

		[Test]
		public void Test_Sort_Comparer_Null()
		{
			var collection = new ViewModelCollection<String>(new[] { "BB", "AA", "CC" });
			_Subscribe(collection);

			// initial state
			Assert.That(collection.Count, Is.EqualTo(3));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(0));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(0));

			// item sorted
			collection.Sort((IComparer<String>)null);

			Assert.That(collection.Count, Is.EqualTo(3));
			Assert.That(collection[0], Is.EqualTo("AA"));
			Assert.That(collection[1], Is.EqualTo("BB"));
			Assert.That(collection[2], Is.EqualTo("CC"));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(0));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(1));
			Assert.That(mCollectionChanges[0].Action, Is.EqualTo(NotifyCollectionChangedAction.Reset));
			Assert.That(mCollectionChanges[0].NewItems, Is.Null);
			Assert.That(mCollectionChanges[0].NewStartingIndex, Is.EqualTo(-1));
			Assert.That(mCollectionChanges[0].OldItems, Is.Null);
			Assert.That(mCollectionChanges[0].OldStartingIndex, Is.EqualTo(-1));
		}

		#endregion

		#region ++ Public Interface (ToArray) ++

		[Test]
		public void Test_ToArray()
		{
			var collection = new ViewModelCollection<String>(new[] { "AA", "BB", "CC" });
			var array = collection.ToArray();

			Assert.That(array, Has.Length.EqualTo(3));
			Assert.That(array[0], Is.EqualTo("AA"));
			Assert.That(array[1], Is.EqualTo("BB"));
			Assert.That(array[2], Is.EqualTo("CC"));
		}

		#endregion

		#region ++ Public Interface (Indexer) ++

		[Test]
		public void Test_Indexer()
		{
			var collection = new ViewModelCollection<String>(new[] { "AA", "BB", "CC" });
			_Subscribe(collection);

			// initial state
			Assert.That(collection.Count, Is.EqualTo(3));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(0));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(0));

			// item replaced
			collection[0] = "A";

			Assert.That(collection.Count, Is.EqualTo(3));
			Assert.That(collection[0], Is.EqualTo("A"));
			Assert.That(collection[1], Is.EqualTo("BB"));
			Assert.That(collection[2], Is.EqualTo("CC"));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(1));
			Assert.That(mPropertyChanges.AsString(";"), Is.EqualTo("Item[]"));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(1));
			Assert.That(mCollectionChanges[0].Action, Is.EqualTo(NotifyCollectionChangedAction.Replace));
			Assert.That(mCollectionChanges[0].NewItems, Is.EquivalentTo(new[] { "A" }));
			Assert.That(mCollectionChanges[0].NewStartingIndex, Is.EqualTo(0));
			Assert.That(mCollectionChanges[0].OldItems, Is.EquivalentTo(new[] { "AA" }));
			Assert.That(mCollectionChanges[0].OldStartingIndex, Is.EqualTo(0));

			// item replaced
			_Reset();
			collection[2] = "C";

			Assert.That(collection.Count, Is.EqualTo(3));
			Assert.That(collection[0], Is.EqualTo("A"));
			Assert.That(collection[1], Is.EqualTo("BB"));
			Assert.That(collection[2], Is.EqualTo("C"));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(1));
			Assert.That(mPropertyChanges.AsString(";"), Is.EqualTo("Item[]"));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(1));
			Assert.That(mCollectionChanges[0].Action, Is.EqualTo(NotifyCollectionChangedAction.Replace));
			Assert.That(mCollectionChanges[0].NewItems, Is.EquivalentTo(new[] { "C" }));
			Assert.That(mCollectionChanges[0].NewStartingIndex, Is.EqualTo(2));
			Assert.That(mCollectionChanges[0].OldItems, Is.EquivalentTo(new[] { "CC" }));
			Assert.That(mCollectionChanges[0].OldStartingIndex, Is.EqualTo(2));
		}

		[Test]
		public void Test_Indexer_Reentrancy()
		{
			var collection = new ViewModelCollection<String>(new[] { "AA", "BB" });

			collection.CollectionChanged += (sender, e) =>
				{
					// not allowed
					collection[1] = "B";
				};

			Assert.Throws<InvalidOperationException>(() =>
				{
					collection[0] = "A";
				});
		}

		#endregion

		#region ++ Public Interface (Move) ++

		[Test]
		public void Test_Move()
		{
			var collection = new ViewModelCollection<String>(new[] { "AA", "BB", "CC" });
			_Subscribe(collection);

			// initial state
			Assert.That(collection.Count, Is.EqualTo(3));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(0));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(0));

			// item moved
			collection.Move(0, 2);

			Assert.That(collection.Count, Is.EqualTo(3));
			Assert.That(collection[0], Is.EqualTo("BB"));
			Assert.That(collection[1], Is.EqualTo("CC"));
			Assert.That(collection[2], Is.EqualTo("AA"));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(1));
			Assert.That(mPropertyChanges.AsString(";"), Is.EqualTo("Item[]"));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(1));
			Assert.That(mCollectionChanges[0].Action, Is.EqualTo(NotifyCollectionChangedAction.Move));
			Assert.That(mCollectionChanges[0].NewItems, Is.EquivalentTo(new[] { "AA" }));
			Assert.That(mCollectionChanges[0].NewStartingIndex, Is.EqualTo(2));
			Assert.That(mCollectionChanges[0].OldItems, Is.EquivalentTo(new[] { "AA" }));
			Assert.That(mCollectionChanges[0].OldStartingIndex, Is.EqualTo(0));

			// item moved
			_Reset();
			collection.Move(2, 2);

			Assert.That(collection.Count, Is.EqualTo(3));
			Assert.That(collection[0], Is.EqualTo("BB"));
			Assert.That(collection[1], Is.EqualTo("CC"));
			Assert.That(collection[2], Is.EqualTo("AA"));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(1));
			Assert.That(mPropertyChanges.AsString(";"), Is.EqualTo("Item[]"));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(1));
			Assert.That(mCollectionChanges[0].Action, Is.EqualTo(NotifyCollectionChangedAction.Move));
			Assert.That(mCollectionChanges[0].NewItems, Is.EquivalentTo(new[] { "AA" }));
			Assert.That(mCollectionChanges[0].NewStartingIndex, Is.EqualTo(2));
			Assert.That(mCollectionChanges[0].OldItems, Is.EquivalentTo(new[] { "AA" }));
			Assert.That(mCollectionChanges[0].OldStartingIndex, Is.EqualTo(2));
		}

		[Test]
		public void Test_Move_InvalidOldIndex()
		{
			var collection = new ViewModelCollection<String>(new[] { "AA", "BB", "CC" });

			Assert.Throws<ArgumentOutOfRangeException>(() =>
				{
					collection.Move(-1, 1);
				});
			Assert.Throws<ArgumentOutOfRangeException>(() =>
				{
					collection.Move(3, 1);
				});
		}

		[Test]
		public void Test_Move_InvalidNewIndex()
		{
			var collection = new ViewModelCollection<String>(new[] { "AA", "BB", "CC" });

			Assert.Throws<ArgumentOutOfRangeException>(() =>
				{
					collection.Move(1, -1);
				});
			Assert.Throws<ArgumentOutOfRangeException>(() =>
				{
					collection.Move(1, 3);
				});
		}

		#endregion

		#region ++ Public Interface (BeginUpdate, EndUpdate) ++

		[Test]
		public void Test_BeginEndUpdate()
		{
			var collection = new ViewModelCollection<String>();
			_Subscribe(collection);

			// initial state
			Assert.That(collection.Count, Is.EqualTo(0));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(0));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(0));

			// item added
			collection.BeginUpdate();
			collection.Add("AA");

			Assert.That(collection.Count, Is.EqualTo(1));
			Assert.That(collection[0], Is.EqualTo("AA"));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(0));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(0));

			// item added
			collection.EndUpdate();
			collection.Add("BB");

			Assert.That(collection.Count, Is.EqualTo(2));
			Assert.That(collection[0], Is.EqualTo("AA"));
			Assert.That(collection[1], Is.EqualTo("BB"));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(3));
			Assert.That(mPropertyChanges.AsString(";"), Is.EqualTo("Count;Item[];HasItems"));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(1));
			Assert.That(mCollectionChanges[0].Action, Is.EqualTo(NotifyCollectionChangedAction.Add));
			Assert.That(mCollectionChanges[0].NewItems, Is.EquivalentTo(new[] { "BB" }));
			Assert.That(mCollectionChanges[0].NewStartingIndex, Is.EqualTo(1));
			Assert.That(mCollectionChanges[0].OldItems, Is.Null);
			Assert.That(mCollectionChanges[0].OldStartingIndex, Is.EqualTo(-1));

			// raise events
			_Reset();
			collection.RaiseCollectionChanged();

			Assert.That(collection.Count, Is.EqualTo(2));
			Assert.That(collection[0], Is.EqualTo("AA"));
			Assert.That(collection[1], Is.EqualTo("BB"));
			Assert.That(mPropertyChanges.Count, Is.EqualTo(3));
			Assert.That(mPropertyChanges.AsString(";"), Is.EqualTo("Count;Item[];HasItems"));
			Assert.That(mCollectionChanges.Count, Is.EqualTo(1));
			Assert.That(mCollectionChanges[0].Action, Is.EqualTo(NotifyCollectionChangedAction.Reset));
			Assert.That(mCollectionChanges[0].NewItems, Is.Null);
			Assert.That(mCollectionChanges[0].NewStartingIndex, Is.EqualTo(-1));
			Assert.That(mCollectionChanges[0].OldItems, Is.Null);
			Assert.That(mCollectionChanges[0].OldStartingIndex, Is.EqualTo(-1));
		}

		#endregion

		#region ++ Public Interface (RaisePropertyChanged) ++

		[Test]
		public void Test_RaisePropertyChanged_Expression()
		{
			var viewModel = new ViewModelCollection<String>();

			var handlerCalled = 0;
			var handlerPropertyName = String.Empty;

			viewModel.PropertyChanged += (sender, e) =>
				{
					handlerPropertyName = e.PropertyName;
					handlerCalled++;
				};

			viewModel.RaisePropertyChanged(() => viewModel.HasItems);

			Assert.That(handlerCalled, Is.EqualTo(1));
			Assert.That(handlerPropertyName, Is.EqualTo("HasItems"));
		}

		[Test]
		public void Test_RaisePropertyChanged_PropertyName()
		{
			var viewModel = new ViewModelCollection<String>();

			var handlerCalled = 0;
			var handlerPropertyName = String.Empty;

			viewModel.PropertyChanged += (sender, e) =>
				{
					handlerPropertyName = e.PropertyName;
					handlerCalled++;
				};

			viewModel.RaisePropertyChanged("HasItems");

			Assert.That(handlerCalled, Is.EqualTo(1));
			Assert.That(handlerPropertyName, Is.EqualTo("HasItems"));
		}

		[Test]
		public void Test_RaisePropertyChanged_PropertyName_NotExisting()
		{
			var viewModel = new ViewModelCollection<String>();

			var handlerCalled = 0;

			viewModel.PropertyChanged += (sender, e) =>
				{
					handlerCalled++;
				};

			Assert.Throws<ArgumentException>(() =>
				viewModel.RaisePropertyChanged("NotExistingProperty"));

			Assert.That(handlerCalled, Is.EqualTo(0));
		}

		[Test]
		public void Test_RaisePropertiesChanged()
		{
			var viewModel = new ViewModelCollection<String>();

			var handlerCalled = 0;
			var handlerPropertyName = String.Empty;

			viewModel.PropertyChanged += (sender, e) =>
				{
					handlerPropertyName = e.PropertyName;
					handlerCalled++;
				};

			viewModel.RaisePropertiesChanged();

			Assert.That(handlerCalled, Is.EqualTo(1));
			Assert.That(handlerPropertyName, Is.EqualTo(null));
		}

		#endregion

	}
}

#endif
